// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Xml.Linq; using JetBrains.Annotations; using LargoCommon.Abstract; using LargoCommon.Localization; using LargoCommon.Midi; namespace LargoCommon.Music { /// /// Musical File. /// [Serializable] public sealed class MusicalBundle { #region Fields /// /// Name of the model. /// private string fileName; /// /// Musical Blocks. /// private Collection blocks; #endregion #region Constructors /// /// Initializes a new instance of the class. /// public MusicalBundle() { this.blocks = new Collection(); this.FileHeading = new FileHeading(); } /// /// Initializes a new instance of the class. /// /// The marked file. public MusicalBundle(XElement xbundle) : this() { Contract.Requires(xbundle != null); if (xbundle == null) { return; } this.FileName = (string)xbundle.Attribute("FileName"); XElement xheading = xbundle.Element("Heading"); this.FileHeading = new FileHeading(xheading); XElement xblocks = xbundle.Element("Blocks"); if (xblocks == null) { return; } //// Blocks foreach (var xblock in xblocks.Elements()) { MusicalBlock block = new MusicalBlock(xblock) { MusicalBundle = this //// FileName = this.Name }; this.Blocks.Add(block); } } #endregion /// /// Gets or sets the maximum number of bars in block. /// /// /// The maximum number of bars in block. /// public static int MaxNumberOfBarsInBlock { get; set; } //// CA1044 (FxCop) #region Properties - Xml /// Gets Xml representation. /// Property description. public XElement GetXElement { get { XElement xbundle = new XElement( "MIF", new XAttribute("FileName", this.FileName)); XElement xcopyright = new XElement( "Software", new XAttribute("Name", MusicalSettings.ApplicationName), new XAttribute("Www", MusicalSettings.ApplicationWeb)); xbundle.Add(xcopyright); var xheading = this.FileHeading.GetXElement; xbundle.Add(xheading); //// Blocks XElement xblocks = new XElement("Blocks"); foreach (MusicalBlock block in this.Blocks.Where(block => block != null)) { var xblock = block.GetXElement; xblocks.Add(xblock); } xbundle.Add(xblocks); return xbundle; } } #endregion #region Public Properties /// /// Gets or sets the original path. /// /// /// The original path. /// public string OriginalPath { get; set; } /// /// Gets the blocks. /// /// /// The blocks. /// public Collection Blocks { get { Contract.Ensures(Contract.Result>() != null); return this.blocks; } } /// /// Gets or sets the type of the model. /// /// /// The type of the model. /// public MusicalOriginType OriginType { get; set; } //// CA1044 (FxCop) /// /// Gets or sets the file heading. /// /// /// The file heading. /// public FileHeading FileHeading { get; set; } #endregion #region Public Properties - Naming /// Gets or sets file name. /// Property description. /// Returns value. public string FileName { get { Contract.Ensures(Contract.Result() != null); if (this.fileName == null) { throw new InvalidOperationException("File name is null."); } return this.fileName; } set { if (value == null) { this.fileName = string.Empty; //// throw new ArgumentException("Name cannot be set null.", "value"); return; } this.fileName = value; } } /// /// Gets the actual name. /// /// Property description. [UsedImplicitly] public string ActualName => string.Format( CultureInfo.CurrentCulture, "{0}_{1}{2}", this.NameAndOrigin, SupportCommon.DateTimeIdentifier, this.OrchestrationName.ClearSpecialChars() ?? string.Empty).Trim(); /// Gets or sets ComposerId. /// Property description. /// Returns value. public int? ComposerId { get; set; } //// CA1044 (FxCop) #endregion #region Public properties /// /// Gets Musical Identification. /// /// Property description. [UsedImplicitly] public IList Identification { get { var items = new List(); var item = new KeyValuePair("FileName", this.FileName); items.Add(item); //// items.AddRange(this.FileHeading.Identification); return items; } } /// /// Gets the identification string. /// /// /// The identification string. /// [UsedImplicitly] public string IdentificationString { get { StringBuilder sb = new StringBuilder(); var idents = this.Identification; foreach (var ident in idents) { sb.AppendFormat("{0}: {1}\n", ident.Key, ident.Value); } foreach (var block in this.blocks) { sb.AppendLine(string.Empty); sb.AppendFormat("{0}/ {1}\n", block.Header.Number, block.Header.Name); sb.Append(block.IdentificationString); } return sb.ToString(); } } /// /// Gets or sets a value indicating whether Is Selected. /// /// Property description. [UsedImplicitly] public bool IsSelected { get; set; } #endregion #region Private properties /// /// Gets the name and origin. /// /// Property description. private string NameAndOrigin { get { var origin = this.OriginType != MusicalOriginType.None ? this.OriginType.ToString() : string.Empty; var clearName = string.Format(CultureInfo.CurrentCulture, "{0}{1}", this.FileName.ClearSpecialChars(), origin); return clearName; } } /// /// Gets or sets the name of the orchestration. /// /// /// The name of the orchestration. /// // ReSharper disable once UnusedAutoPropertyAccessor.Local private string OrchestrationName { get; set; } #endregion #region Static factory methods /// /// Gets the new musical file. /// /// The file name. /// Returns value. public static MusicalBundle GetNewMusicalBundle(string fileName) { var musicBundle = new MusicalBundle { FileName = fileName, blocks = new Collection(), FileHeading = { WorkTitle = fileName } }; return musicBundle; } /// /// Gets the new musical file. /// /// The musical block. /// The file name. /// /// Returns value. /// public static MusicalBundle GetEnvelopeOfBlock(MusicalBlock musicalBlock, string givenFileName) { var musicBundle = new MusicalBundle { FileName = givenFileName, blocks = new Collection(), FileHeading = { WorkTitle = givenFileName } }; if (musicalBlock != null) { musicBundle.Blocks.Add(musicalBlock); musicalBlock.MusicalBundle = musicBundle; } return musicBundle; } /// /// Load MIDI File. /// /// The file path. /// Name of the internal. /// If set to true [split multi-voice tracks]. /// /// Returns value. /// public static MusicalBundle ReadMidiFile(string filePath, string internalName, FileSplit splitMultivoiceTracks) { Contract.Requires(filePath != null); if (string.IsNullOrEmpty(filePath)) { return null; } ProcessLogger.Singleton.SendLogEvent(Path.GetFileName(filePath), LocalizedMusic.String("Reading Midi file ... "), 0); var midiFile = new MidiFile(filePath); var sequence = midiFile.Sequence; sequence.InternalName = internalName; return GetMusicalBundle(sequence, splitMultivoiceTracks); } /// /// Musicals the file. /// /// The sequence. /// The split multi-voice tracks. /// /// Returns value. /// public static MusicalBundle GetMusicalBundle(CompactMidiStrip givenSequence, FileSplit splitMultivoiceTracks) { Contract.Requires(givenSequence != null); const byte harmonicOrder = DefaultValue.HarmonicOrder; //// Track can be saved only after back writing of events var musicalBundle = GetNewMusicalBundle(givenSequence.InternalName); musicalBundle.OriginType = MusicalOriginType.Original; if (givenSequence.Format == 0) { givenSequence = givenSequence.SplitTracksByInstruments(); givenSequence.SetTrackInstrumentsFromFirstOccurrence(); } var midiBlocks = givenSequence.GetMidiBlocks(MaxNumberOfBarsInBlock); foreach (var midiBlock in midiBlocks) { var rhythmicOrder = MusicalProperties.RhythmicOrder(midiBlock.Header.Division, midiBlock.Header.Metric.MetricBeat, midiBlock.Header.Metric.MetricGround); var block = MusicalBlock.NewMusicalBlock((MidiBlock)midiBlock, harmonicOrder, rhythmicOrder); /* sequence */ //// block.FileName = musicalBundle.Name; block.TonalityKey = midiBlock.TonalityKey; block.TonalityGenus = midiBlock.TonalityGenus; if (block.Header.NumberOfMelodicLines > 0) { MusicalLinearizer linearizer = new MusicalLinearizer(block.Header) { Lines = block.Strip.Lines }; linearizer.SplitTracksToParts(block, splitMultivoiceTracks); linearizer.TransferPartsToTracks(true); block.Strip.SetLines(linearizer.Lines); block.Strip.RebuildChannels(); block.LoadFirstStatusToLines(); //// 2019/10 } //// Skip block having only negligible tracks!? (2019/03) if (!block.Strip.Lines.Any()) { continue; } block.ConvertStripToBody(false); var toneTracks = (from mt in ((MidiBlock)midiBlock).Sequence where mt.Sequence != null select mt) .ToList(); block.Body.SetTempoEventsFrom(midiBlock.MidiTimeFrom, midiBlock.MidiTimeTo, toneTracks); //// Status (lists of bars in tracks) will be transformed to blockStatus. //// List of bar status in each given line have to be converted to Status in elements. //// Read from midi file needs status from tones...!? block.Body.SetBodyStatusFromTones(); //// MusicMidiWriter writer = new MusicMidiWriter(block); //// CompactMidiStrip midiTracks = writer.WriteToSequence(false); //// staff grouping //// block.SplitTracksToParts(splitMultivoiceTracks); //// block.TransferPartsToTracks(); //// MusicMidiWriter.SaveBlockMidiToDatabase(block); block.MusicalBundle = musicalBundle; musicalBundle.Blocks.Add(block); } //// 2019/01 musicalBundle.SaveToMidi(false, splitMultivoiceTracks); //// MusicMidiWriter.SaveMidiToDatabase(file, originalName + " (i-split)"); musicalBundle.FileName = givenSequence.InternalName; return musicalBundle; } #endregion #region Public methods /// Makes a deep copy of the MusicalFile object. /// Returns object. [UsedImplicitly] public object Clone() { var file = GetNewMusicalBundle(this.FileName); file.ComposerId = this.ComposerId; file.FileHeading.Encoder = this.FileHeading.Encoder; file.FileHeading.EncodingDate = this.FileHeading.EncodingDate; file.FileHeading.EncodingDescription = this.FileHeading.EncodingDescription; //// file.Identification = this.Identification; file.FileHeading.Software = this.FileHeading.Software; file.FileHeading.WorkNumber = this.FileHeading.WorkNumber; file.FileHeading.WorkTitle = this.FileHeading.WorkTitle; //// file.Blocks = new Collection(); foreach (var newBlock in this.Blocks.Select(block => block.Clone(true, true))) { file.Blocks.Add(newBlock); newBlock.MusicalBundle = file; } return file; } /// /// Sorts the blocks. /// [UsedImplicitly] public void SortBlocks() { var bs = (from fb in this.Blocks orderby fb.Header.Number select fb).ToList(); this.blocks = new Collection(bs); } /// /// Saves the midi of musical file. /// /// If set to true [check tracks]. /// The split multi-voice tracks. public void SaveToMidi(bool checkTracks, FileSplit splitMultivoiceTracks) { foreach (var block in this.Blocks.Where(block => block.Strip.Lines.Any())) { if (checkTracks) { //// this primarily splits percussion track according to instruments //// bool split = splitMultivoiceTracks == FileSplit.Total || (splitMultivoiceTracks == FileSplit.Automatic && block.Strip.Lines.Count < 3); MusicalLinearizer linearizer = new MusicalLinearizer(block.Header) { Lines = block.Strip.Lines }; linearizer.SplitTracksToParts(block, splitMultivoiceTracks); linearizer.TransferPartsToTracks(true); block.Strip.SetLines(linearizer.Lines); block.Strip.RebuildChannels(); } ////201508 SaveMidiOfMusicalBlock(block, MidiSourceType.ReadBlock); } } #endregion } }